package xfile

import (
	"bufio"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"gitee.com/xuesongtao/gotool/base"
)

type FileInfo struct {
	AbsolutePath string // 本地绝对路径
	RelativePath string // 相对项目的路径
	Filename     string // 文件名
}

func (f *FileInfo) GetFilePath() string {
	return filepath.Join(f.RelativePath, f.Filename)
}

// GetProjectDir 获取项目路径
func GetProjectDir(projectName string) string {
	projectDir, _ := os.Getwd()
	splitIndex := strings.LastIndex(projectDir, projectName)
	return projectDir[:splitIndex+len(projectName)]
}

// ParseFileFormat 解析文件格式
func ParseFileFormat(fileName string) string {
	splitIndex := strings.LastIndex(fileName, ".")
	if splitIndex < 0 {
		return ""
	}
	return strings.ToLower(fileName[splitIndex+1:])
}

// CopyFile 复制文件
func CopyFile(src, dst string, isFirstDel ...bool) (err error) {
	if src == "" {
		return errors.New("source file cannot be empty")
	}

	if dst == "" {
		return errors.New("destination file cannot be empty")
	}

	// 如果相同就不处理
	if src == dst {
		return nil
	}

	// 删除原来的
	if len(isFirstDel) > 0 && isFirstDel[0] {
		if err = os.Remove(dst); err != nil {
			return
		}
	}

	in, err := os.Open(src)
	if err != nil {
		return
	}
	defer func() {
		if e := in.Close(); e != nil {
			err = e
		}
	}()

	out, err := Create(dst)
	if err != nil {
		return
	}
	defer func() {
		if e := out.Close(); e != nil {
			err = e
		}
	}()

	// 复制
	if _, err = io.Copy(out, in); err != nil {
		return
	}

	// 写盘
	if err = out.Sync(); err != nil {
		return
	}

	// 调整权限
	if err = os.Chmod(dst, os.FileMode(0777)); err != nil {
		return
	}
	return
}

// Create 可以新建文件/目录
// 注: 需要外部调用 Close 进行关闭
func Create(path string) (*os.File, error) {
	dir := filepath.Dir(path)
	if !Exists(dir) { // 判断下目录是否存在, 不存在就先创建目录
		if err := os.MkdirAll(dir, os.ModePerm); err != nil {
			return nil, err
		}
	}
	return os.Create(path)
}

// Exists 判断文件/目录是否存在
func Exists(path string) bool {
	if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) {
		return true
	}
	return false
}

// Open
func Open(path string) (*os.File, error) {
	return os.Open(path)
}

// OpenFile
func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
	return os.OpenFile(path, flag, perm)
}

// AppendContent 向文件里追加内容
func AppendContent(path string, content string) (int, error) {
	hf := NewFileHandle(path)
	if err := hf.Initf(os.O_WRONLY | os.O_APPEND); err != nil {
		return 0, err
	}

	f := hf.GetFile()
	defer hf.Close()
	return f.WriteString(content)
}

// PutContents 向文件里追加内容
func PutContent(path string, content string) (int, error) {
	hf := NewFileHandle(path)
	if err := hf.Initf(os.O_WRONLY | os.O_TRUNC); err != nil {
		return 0, err
	}

	f := hf.GetFile()
	defer hf.Close()
	return f.WriteString(content)
}

// GetContent 获取文件内容
func GetContent(path string) (string, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

// Remove 移除
func Remove(path string) error {
	return os.RemoveAll(path)
}

// CronParseLogFile 定时解析增量 log, 返回每行的内容
func CronParseLogFile(collectLog string, offset ...int) ([]string, error) {
	var (
		logDir, filename string
		tmpIndex         = strings.LastIndex(collectLog, "/")
	)
	copyLog := collectLog
	if tmpIndex != -1 {
		logDir = copyLog[:tmpIndex]
		filename = copyLog[tmpIndex+1:]
	}
	if logDir == "" {
		logDir = "."
		filename = copyLog
	}

	// 解析名
	tmpIndex = strings.LastIndex(filename, ".")
	if tmpIndex == -1 {
		return nil, errors.New("collectLog is not ok")
	}

	// 新的文件名
	filename = filename[:tmpIndex] + ".txt"

	// 处理程序日志
	offsetTxtDir := logDir + "/.offset/"
	collectLogOffsetTxtPath := offsetTxtDir + filename

	// 判断待收集文件是否存在
	// 因为有可能出现待收集的 log 会出现被重置, 所以这里判断当前偏移量是否大于待收集的文件大小, 如果大于说明待收集文件已被重置, 这里重置偏移量, 重新收集
	stat, err := os.Stat(collectLog)
	if os.IsNotExist(err) {
		return nil, errors.New("collectLog log is not exist")
	}

	// 获取偏移量
	// 获取待处理的偏移量 log
	if !Exists(collectLogOffsetTxtPath) { // 不存在的话就新建
		_, err := PutContent(collectLogOffsetTxtPath, "0")
		if err != nil {
			return nil, err
		}
	}
	// 存在的话就直接过去读取的偏移量
	var offsetInt int
	if len(offset) > 0 {
		offsetInt = offset[0]
	} else {
		offsetStr, err := GetContent(collectLogOffsetTxtPath)
		if err != nil {
			return nil, err
		}
		offsetInt, _ = strconv.Atoi(offsetStr)
	}

	// 如果大于说明待收集文件已被重置, 这里重置偏移量
	if offsetInt > int(stat.Size()) {
		offsetInt = 0
	}
	// fmt.Printf("accessLog: %s, offsetLog: %s, offsetInt: %d", accessLogPath, accessLogOffsetLogPath, offsetInt)

	f, err := Open(collectLog)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	// 获取当前文件信息
	fileInfo, err := f.Stat()
	if err != nil {
		return nil, err
	}

	// 设置偏移量
	_, err = f.Seek(int64(offsetInt), io.SeekStart)
	if err != nil {
		return nil, err
	}

	// 逐行读取
	rows := bufio.NewScanner(f)
	result := make([]string, 0, 100)
	for rows.Scan() {
		rowStr := rows.Text()
		if rowStr == "" {
			continue
		}
		result = append(result, rowStr)
	}

	// 设置处理完的偏移量
	curOffset := fileInfo.Size()
	_, err = PutContent(collectLogOffsetTxtPath, base.ToString(curOffset))
	if err != nil {
		return nil, err
	}

	// 删除其他的偏移量
	files, err := ioutil.ReadDir(offsetTxtDir)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		name := file.Name()
		if name == filename {
			continue
		}
		if err := Remove(offsetTxtDir + name); err != nil {
			return nil, err
		}
	}
	return result, nil
}
